﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;

namespace SrdBasic
{
    /**
     * These rules use the following die notations:

        •	d4 = four sided die
        •	d6 = six sided die
        •	d8 = eight sided die
        •	d10 = ten sided die
        •	d12 = twelve sided die
        •	d20 = twenty sided die
        •	d% = percentile dice

     * Die rolls are expressed in the format:
     * [#] die type [+/- modifiers]
     * 
     * reference : http://www.opengamingfoundation.org/srd.html
     * 
    */

    public class DiceNotation
    {
        private RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();

        #region Public Members
        
        public static int D4(int count, int modifiers)
        {
            return Dice(count, 4, modifiers);
        }

        public static int D6(int count, int modifiers)
        {
            return Dice(count, 6, modifiers);
        }

        public static int D8(int count, int modifiers)
        {
            return Dice(count, 8, modifiers);
        }

        public static int D10(int count, int modifiers)
        {
            return Dice(count, 10, modifiers);
        }

        public static int D12(int count, int modifiers)
        {
            return Dice(count, 12, modifiers);
        }

        public static int D20(int count, int modifiers)
        {
            return Dice(count, 20, modifiers);
        }

        public static int DPercent()
        {
            return Dice(1, 100, 0);
        }

        #endregion

        #region Private Members

        private static int Dice(int n, int sides, int modifiers)
        {
            int result = 0;
            for (int i = 0; i < n; i++)
            {
               result += (int)RollDice((byte)sides);
            }
            return result + modifiers;
        }


        // from msdn e...
        private static byte RollDice(byte numberSides)
        {
            if (numberSides <= 0)
                throw new ArgumentOutOfRangeException("numberSides");
            // Create a new instance of the RNGCryptoServiceProvider.
            RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider();
            // Create a byte array to hold the random value.
            byte[] randomNumber = new byte[1];
            do
            {
                // Fill the array with a random value.
                rngCsp.GetBytes(randomNumber);
            }
            while (!IsFairRoll(randomNumber[0], numberSides));
            // Return the random number mod the number
            // of sides.  The possible values are zero-
            // based, so we add one.
            return (byte)((randomNumber[0] % numberSides) + 1);
        }

        private static bool IsFairRoll(byte roll, byte numSides)
        {
            // There are MaxValue / numSides full sets of numbers that can come up
            // in a single byte.  For instance, if we have a 6 sided die, there are
            // 42 full sets of 1-6 that come up.  The 43rd set is incomplete.
            int fullSetsOfValues = Byte.MaxValue / numSides;

            // If the roll is within this range of fair values, then we let it continue.
            // In the 6 sided die case, a roll between 0 and 251 is allowed.  (We use
            // < rather than <= since the = portion allows through an extra 0 value).
            // 252 through 255 would provide an extra 0, 1, 2, 3 so they are not fair
            // to use.
            return roll < numSides * fullSetsOfValues;
        }

        #endregion

    }
}
